/*
 *  FAT Initialization
 *
 *  Copyright(C) 2001
 *  Camilo Alejandro Arboleda
 *
 *  The contents of this file are distributed under the GNU General
 *  Public License version 2.
 *
 *  As a special exception, when this code is included in the RTEMS
 *  operating system, linking other files with RTEMS objects including
 *  this code does not cause the resulting executable application to
 *  be covered by the GNU General Public License. This exception does
 *  not however invalidate any other reasons why the executable file might
 *  be covered by the GNU General Public License.
 */

#include <sys/types.h>         /* for mkdir */
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#include <assert.h>

#include "fat.h"
#include "mtd.h"

#if defined(FAT_DEBUG)
#include <stdio.h>
#endif

rtems_filesystem_limits_and_options_t FAT_LIMITS_AND_OPTIONS = {
   1,	/* link_max */
   1,	/* max_canon */
   1,	/* max_input */
   FAT_NAME_MAX,	/* name_max */
   63,	/* path_max */
   1,	/* pipe_buf */
   1,	/* posix_async_io */
   1,	/* posix_chown_restrictions */
   1,	/* posix_no_trunc */
   1,	/* posix_prio_io */
   1,	/* posix_sync_io */
   1	/* posix_vdisable */
};

extern FAT_jnode_t *fat_root_dir;
extern FAT_jnode_t *fat_tail;

/*
 * Reads FAT MBR (master boot record)
 */
void
read_mbr(fat_geom_t *fat_geom, int fd)
{
   unsigned char buffer[512];

   read(fd, buffer, 512);         /* MBR  */
   fat_geom->bytes_per_sect       = * (unsigned short *) (buffer + 11);
   fat_geom->cluster_size         = * (buffer + 13);
   fat_geom->reserved_sect        = * (unsigned short *) (buffer + 14);
   fat_geom->total_FATs           = * (buffer + 16);
   fat_geom->root_dir_entries     = * (unsigned short *) (buffer + 17);
   fat_geom->logical_sectors      = * (unsigned short *) (buffer + 19);
   fat_geom->mdb                  = * (buffer + 21);
   fat_geom->sectors_per_fat      = * (unsigned short *) (buffer + 22);
   fat_geom->sectors_per_track    = * (unsigned short *) (buffer + 24);
   fat_geom->head                 = * (unsigned short *) (buffer + 26);
   fat_geom->hidden_sectors       = * (unsigned short *) (buffer + 28);
   fat_geom->root_dir_offset      = (fat_geom->total_FATs * fat_geom->sectors_per_fat + 1) * fat_geom->bytes_per_sect;
   fat_geom->data_offset          = fat_geom->root_dir_offset + (fat_geom->root_dir_entries * 32);
   fat_geom->data_size            = fat_geom->logical_sectors * fat_geom->bytes_per_sect - fat_geom->data_offset;
   fat_geom->cluster_size        *= fat_geom->bytes_per_sect; /* cluster size in bytes */
   fat_geom->first_cluster_offset = fat_geom->data_offset - (fat_geom->cluster_size * 2);
   fat_geom->fat_entries          = fat_geom->data_size / fat_geom->cluster_size;
   fat_geom->type                 = 1; /* By default, FAT12 */
#ifdef FAT_USE_DoC
   /* This is a hack used to get partition type (FAT12/FAT16/FAT32)
    * A real *standard* ioctl should be put here to get this info
    */
   ioctl(fd,PARTGETTYPE,buffer);
   fat_geom->type                 = *buffer;
#endif
}

/*
 *  FAT_initialize
 */

int
FAT_initialize(rtems_filesystem_mount_table_entry_t *mt_entry)
{
   FAT_jnode_t  *node;
   fat_geom_t   *geom;
   rtems_filesystem_operations_table    *op_table = &FAT_ops;
   rtems_filesystem_file_handlers_r     *root_handlers = &FAT_directory_handlers;
   int fd;
   unsigned status;


   /* Open hard disk device */
   if (!mt_entry->dev)
      return -1;
   fd = open(mt_entry->dev,O_RDWR);
   if (fd <= 0)
      return -1;

   /* Read FAT_Geometry */
   geom = calloc(1,sizeof(fat_geom_t));
   if (!geom)
      return -1;
   read_mbr(geom,fd);
   geom->fd = fd;

   status = rtems_semaphore_create(rtems_build_name('F','D','S','0'),1,
                                   RTEMS_BINARY_SEMAPHORE,
                                   RTEMS_PRIORITY,&(geom->mutex_id));
   if (status != RTEMS_SUCCESSFUL) {
      free(geom);
      return -1;
   }

   status = rtems_semaphore_create(rtems_build_name('R','W','M','T'),1,
                                   RTEMS_BINARY_SEMAPHORE,
                                   RTEMS_PRIORITY,&(geom->rw_mutex_id));
   if (status != RTEMS_SUCCESSFUL) {
      free(geom);
      return -1;
   }

   node = calloc(1,sizeof(FAT_jnode_t));
   if (!node) {
      free(geom);
      return -1;
   }

   mt_entry->mt_fs_root.handlers         = root_handlers;
   mt_entry->mt_fs_root.ops              = op_table;
   mt_entry->mt_fs_root.node_access      = node;
   mt_entry->mt_fs_root.mt_entry         = mt_entry;
   mt_entry->pathconf_limits_and_options = FAT_LIMITS_AND_OPTIONS;
   mt_entry->fs_info                     = (void *) geom;

   node->st_ino    = 0; /* root dir */
   node->parent    = NULL;
   node->type      = FAT_DIRECTORY;
   node->offset    = 0;
   node->reference = 1;
   node->attrib    = 0x30;
   node->cache_offset = -1;
   node->geometry  = geom;
   fat_root_dir = node;
   fat_tail = node;

   /* Init FAT_cache
    */
   read_fat_block(0,geom);
   return 0;
}
